
import NotifyMgr from "../notify/NotifyMgr";
import Entity from "./Entity";
import IComponent from "./IComponent";

enum EntityOperationEvent
{
    CreatePlayer,
    CreateBullet,
    CreateBox,
    RemoveBox,
}

export default class EntityWorld
{
    isActive:boolean = true;

    entities:Map<string,Entity>;
    typeAllComponents:Map<string, Array<IComponent>>; // key component type
    entityAllComponents:Map<string, Array<IComponent>>; // key entity_id

    private constructor()
    {
        this.typeAllComponents = new Map<string, Array<IComponent>>();
        this.entityAllComponents = new Map<string, Array<IComponent>>();
        this.entities = new Map<string, Entity>();
    }

    public Reset():void
    {
        this.entities.forEach((entity:Entity, enitiyId:string)=>{
            entity.world = null;
            Entity.ObjectPool.Return(entity);
        });
        this.entities.clear();
        this.typeAllComponents.clear();
        this.entityAllComponents.clear();
    }
    public GetEntities():Array<Entity>
    {
        let ret = [];
        this.entities.forEach((entity:Entity, enitiyId:string)=>{
            ret.push(entity);
        });
        return ret;
    }

    GetComponentByEntityId(entityId:string, componentType:string):IComponent
    {
        if (this.entityAllComponents.has(entityId))
        {
            let components = this.entityAllComponents.get(entityId);
            for (let i = components.length - 1; i > -1; --i)
            {
                if (componentType === components[i].GetType())
                    return components[i];
            }
        }
        return null;
    }

    GetComponents(type:string) : Array<IComponent>
    {
        if (this.typeAllComponents.has(type))
        {
            return this.typeAllComponents.get(type);
        }
        return [];
    }
    AddComponent(component:IComponent):void
    {
        let type = component.GetType();
        if (!this.typeAllComponents.has(type))
            this.typeAllComponents.set(type, new Array<IComponent>());
        if (this.typeAllComponents.get(type).indexOf(component) === -1)
            this.typeAllComponents.get(type).push(component);

        if (!this.entityAllComponents.has(component.entityId))
            this.entityAllComponents.set(component.entityId,new Array<IComponent>());
        if (this.entityAllComponents.get(component.entityId).indexOf(component) === -1)
            this.entityAllComponents.get(component.entityId).push(component);
    }
    RemoveComponent(component:IComponent):void
    {
        let type = component.GetType();
        if (this.typeAllComponents.has(type))
        {
            let index = this.typeAllComponents.get(type).indexOf(component);
            if (index !== -1)
                this.typeAllComponents.get(type).splice(index, 1);
        }

        if (this.entityAllComponents.has(component.entityId))
        {
            let index = this.entityAllComponents.get(component.entityId).indexOf(component);
            if (index !== -1)
                this.entityAllComponents.get(component.entityId).splice(index, 1);
        }
    }
    public static Create():EntityWorld
    {
        return new EntityWorld();
    }


    public ContainEntity(entityId:string): boolean
    {
        return this.entities.has(entityId);
    }

    public AddEntity(entityId:string): Entity
    {
        if (!this.ContainEntity(entityId))
        {
            let entity = Entity.ObjectPool.Get() as Entity;
            entity.id = entityId;
            this.entities.set(entityId, entity);
            entity.world = this;
            return entity;
        }
        return this.entities.get(entityId);
    }
    public RemoveEntity(entityId: string): boolean
    {
        let entity = this.GetEntity(entityId);
        if (entity != null)
        {
            this.entities.delete(entityId);
            entity.world = null;
            this.RemoveEntityComponentAll(entityId);
            Entity.ObjectPool.Return(entity);
            entity = null;
            return true;
        }
        return false;
    }

    private RemoveEntityComponentAll(entityId:string):void 
    {
        if (this.entityAllComponents.has(entityId))
        {
            let components = this.entityAllComponents.get(entityId);
            if (components != null)
            {
                for (let i = components.length - 1; i > -1; --i)
                {
                    let com = components[i];
                    if (com != null) this.RemoveComponent(com);
                }
            }
            this.entityAllComponents.delete(entityId);
        }
    }
    public GetEntity(id:string):Entity
    {
        if (this.ContainEntity(id))
            return this.entities.get(id);
        return null;
    }

    public FindAllEntitiesIds(): Array<string> 
    {
        let ids = new Array<string>();
        this.entities.forEach((value, key)=>{
            ids.push(value.id);
        })
        ids.sort((a, b) => a.localeCompare(b));
        return ids;
    }

    public FindAllCloneComponents(): Array<IComponent> 
    {
        let components = new Array<IComponent>();
        this.typeAllComponents.forEach((values, key)=>
        {
            values.forEach(comp =>{
                components.push(comp.Clone());
            });
        });
        components.sort((a,b)=> a.entityId.localeCompare(b.entityId));
        return components;
    }
    
    // public RollBack(data:EntityWorldFrameData, collection:PtKeyFrameCollection):void
    // {
    //     Reset();
    //     foreach(Guid entityId in data.EntityIds)
    //     {
    //         Entity entity = AddEntity(entityId);               
    //         foreach (IComponent com in data.Components)
    //         {
    //             if(com.EntityId == entityId)
    //             {
    //                 foreach(FrameIdxInfo info in collection.KeyFrames)
    //                 {
    //                     if(info.EqualsInfo(com))
    //                     {
    //                         IParamsUpdatable updatableCom = com as IParamsUpdatable;
    //                         if (updatableCom != null)
    //                             updatableCom.UpdateParams(info.Params);
    //                         else
    //                             throw new Exception("Component " + com.ToString() + " must be IParamsUpdatable");
    //                         break;
    //                     }
    //                 }
    //                 entity.AddComponent(com);
    //             }                         
    //         }                                    
    //     };

    //     foreach(FrameIdxInfo info in collection.KeyFrames)
    //     {
    //         if(!ContainEntity(info.EntityId))
    //         {
    //             if(info.Cmd == FrameCommand.SYNC_CREATE_ENTITY)
    //             {
    //                 NotifyCreateEntity(info);
    //             }      
    //         }
    //         else
    //         {
    //             if (info.Cmd == FrameCommand.SYNC_REMOVE_ENTITY)
    //             {
    //                 NotifyRemoveEntity(info.EntityId);
    //             }
    //         }
    //     }
    // }


    // NotifyCreateEntity(info:FrameIdxInfo):void
    // {
    //     Entity entity = AddEntity(info.EntityId);
    //     if(entity != null)
    //     {          
    //         m_Notifier.Send((EntityOperationEvent)int.Parse(info.Params[0]), entity,info);
    //     }
    // }
    // NotifyRemoveEntity(entityId:string):void
    // {
    //     RemoveEntity(entityId);
    //     m_Notifier.Send(EntityOperationEvent.RemoveBox, entityId);
    // }
}
